using System;
using System.ComponentModel;
using System.Linq;
using LinqExpression = System.Linq.Expressions;
using System.Windows;
using System.Windows.Input;
using SoGame.Helper;
using System.Configuration;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Reflection;

namespace SoGame.ViewModels
{
    public abstract class BaseViewModel : INotifyPropertyChanged, IDataErrorInfo
    {
        #region WindowPropertys

        public void ShowMessageBox(string message)
        {
            MessageBox.Show(message, "", MessageBoxButton.OK, MessageBoxImage.Error);
        }

        public ICommand Close
        {
            get { return new BaseCommand(CloseApplication); }
        }

        public ICommand Maximice
        {
            get { return new BaseCommand(MaximiceApplication); }
        }

        public ICommand Minimice
        {
            get { return new BaseCommand(MinimiceApplication); }
        }

        public ICommand DragMove
        {
            get { return new BaseCommand(DragMoveCommand); }
        }

        public ICommand Restart
        {
            get { return new BaseCommand(RestartCommand); }
        }

        private static void RestartCommand()
        {
            Application.Current.Shutdown();
        }

        private static void DragMoveCommand()
        {
            Application.Current.MainWindow.DragMove();
        }

        private static void CloseApplication()
        {
            Application.Current.Shutdown();
        }

        private static void MaximiceApplication()
        {
            if (Application.Current.MainWindow.WindowState == WindowState.Maximized)
                Application.Current.MainWindow.WindowState = WindowState.Normal;
            else
                Application.Current.MainWindow.WindowState = WindowState.Maximized;
        }

        private static void MinimiceApplication()
        {
            if (Application.Current.MainWindow.WindowState == WindowState.Minimized)
            {
                Application.Current.MainWindow.Opacity = 1;
                Application.Current.MainWindow.WindowState = WindowState.Normal;
            }
            else
            {
                Application.Current.MainWindow.Opacity = 0;
                Application.Current.MainWindow.WindowState = WindowState.Minimized;
            }
        }

        #endregion

        #region Base Properties
        /// <summary>
        /// Gets a value indicating whether [enable send email].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [enable send email]; otherwise, <c>false</c>.
        /// </value>
        public bool EnableSendEmail
        {
            get { return Convert.ToBoolean(ConfigurationManager.AppSettings["EnableSendEmail"]); }
        }

        public bool AutoconnectFacebook {
            get { return Convert.ToBoolean(ConfigurationManager.AppSettings["AutoconnectFacebook"]); }
        }

        /// <summary>
        /// Gets the email interval.
        /// </summary>
        public int EmailInterval
        {
            get { return Convert.ToInt32(ConfigurationManager.AppSettings["SendEmailInterval"]); }
        }

        /// <summary>
        /// Gets the SMTP server.
        /// </summary>
        public string SmtpServer
        {
            get { return ConfigurationManager.AppSettings["SmtpServer"]; }
        }

        /// <summary>
        /// Gets the SMTP port.
        /// </summary>
        public int SmtpPort
        {
            get { return Convert.ToInt32(ConfigurationManager.AppSettings["SmtpPort"]); }
        }

        /// <summary>
        /// Gets the SMTP user.
        /// </summary>
        public string SmtpUser
        {
            get { return ConfigurationManager.AppSettings["SmtpUser"]; }
        }

        /// <summary>
        /// Gets the SMTP password.
        /// </summary>
        public string SmtpPassword
        {
            get { return ConfigurationManager.AppSettings["SmtpPassword"]; }
        }

        /// <summary>
        /// Gets the mail from.
        /// </summary>
        public string MailFrom
        {
            get { return ConfigurationManager.AppSettings["MailFrom"]; }
        }

        /// <summary>
        /// Gets the name of the mail from.
        /// </summary>
        /// <value>
        /// The name of the mail from.
        /// </value>
        public string MailFromName
        {
            get { return ConfigurationManager.AppSettings["MailFromName"]; }
        }

        /// <summary>
        /// Gets a value indicating whether [use kinect].
        /// </summary>
        /// <value>
        ///   <c>true</c> if [use kinect]; otherwise, <c>false</c>.
        /// </value>
        public bool UseKinect {
            get { return Convert.ToBoolean(ConfigurationManager.AppSettings["UseKinect"]); }
        }

        /// <summary>
        /// Gets the facebook app id.
        /// </summary>
        public string FacebookAppId {
            get {
                return ConfigurationManager.AppSettings["FacebookAppId"];
            }
        }

        /// <summary>
        /// Gets the name of the facebook account.
        /// </summary>
        /// <value>
        /// The name of the facebook account.
        /// </value>
        public string FacebookAccountName {
            get
            {
                return ConfigurationManager.AppSettings["FacebookAccountName"];
            }
        }
        #endregion

        #region Propertychanged
        protected void OnPropertyChanged<T>(LinqExpression.Expression<Func<T>> action)
        {
            var propertyName = GetPropertyName(action);
            OnPropertyChanged(propertyName);
        }

        private static string GetPropertyName<T>(LinqExpression.Expression<Func<T>> action)
        {
            var expression = (LinqExpression.MemberExpression)action.Body;
            var propertyName = expression.Member.Name;
            return propertyName;
        }

        private void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        #region IDataErrorInfo
        private bool alreadyLoaded = false;
        /// <summary>
        /// Gets the model.
        /// </summary>
        public virtual dynamic Model {
            get { return null; }
        }

        /// <summary>
        /// Ensures the data error info is initialized.
        /// </summary>
        private void EnsureDataErrorInfoInitialize()
        {
            if (this.Model != null && !this.alreadyLoaded) {
                InitializeDataErrorInfo(this.Model);
            }
        }

        private static Dictionary<string, Delegate> propertyGetters;

        private static Dictionary<string, ValidationAttribute[]> validators;

        /// <summary>
        /// Initializes the data error info.
        /// </summary>
        /// <typeparam name="TModel">The type of the model.</typeparam>
        /// <param name="model">The model.</param>
        private void InitializeDataErrorInfo<TModel>(TModel model) 
        {
            var modelType = typeof(TModel);
            propertyGetters = modelType.GetProperties()
                              .Where(p => GetValidations(p).Length != 0)
                              .ToDictionary(p => p.Name, p => GetValueGetter(modelType, p));

            validators = modelType.GetProperties()
            .Where(p => GetValidations(p).Length != 0)
            .ToDictionary(p => p.Name, p => GetValidations(p));

            this.alreadyLoaded = true;
        }


        /// <summary>
        /// Gets the validations.
        /// </summary>
        /// <param name="property">The property.</param>
        /// <returns></returns>
        private static ValidationAttribute[] GetValidations(PropertyInfo property)
        {
            return (ValidationAttribute[])property
                .GetCustomAttributes(typeof(ValidationAttribute), true);
        }

        /// <summary>
        /// Gets the value getter.
        /// </summary>
        /// <param name="t">The t.</param>
        /// <param name="property">The property.</param>
        /// <returns></returns>
        private static Delegate GetValueGetter(Type t, PropertyInfo property)
        {
            var instance = LinqExpression.Expression.Parameter(t, "i");
            //var instance = LinqExpression.Expression.Parameter(typeof(object), "i");
            var cast = LinqExpression.Expression.TypeAs(
                LinqExpression.Expression.Property(instance, property),
                typeof(object));

            var _t = LinqExpression.Expression.Lambda(cast, instance).Compile();
            
            return _t; // as Func<object, object>;
        }

        /// <summary>
        /// Gets an error message indicating what is wrong with this object.
        /// </summary>
        /// <returns>An error message indicating what is wrong with this object. The default is an empty string ("").</returns>
        public string Error
        {
            get {
                var message = new List<string>();

                propertyGetters.ToList().ForEach(
                    pg => {
                        var value = pg.Value.DynamicInvoke(this.Model);
                        var errors = validators[pg.Key].Where(v => !v.IsValid(value))
                            .Select(v => v.ErrorMessage).ToList();
                        message.AddRange(errors);
                });

                return string.Join(Environment.NewLine, message);
            }
        }

        /// <summary>
        /// Gets the error message for the property with the given name.
        /// </summary>
        /// <returns>The error message for the property. The default is an empty string ("").</returns>
        public string this[string columnName]
        {
            get {
                EnsureDataErrorInfoInitialize();
                if (propertyGetters != null && propertyGetters.ContainsKey(columnName))
                {
                    var value = propertyGetters[columnName].DynamicInvoke(this.Model);
                    var errors = validators[columnName].Where(v => !v.IsValid(value))
                        .Select(v => v.ErrorMessage).ToArray();
                    return string.Join(Environment.NewLine, errors);
                }

                return string.Empty;
            }
        }
        #endregion
    }
}